/***************************************************************************
 *   Copyright (C) 2006 by Harish Kukreja                                  *
 *   harish.kukreja@gmail.com                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//
// C++ Interface: world
//
// Description: Declarations for Physiks' 'World' class and its members
//
//
// Author: Harish Kukreja <harish.kukreja@gmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//


#ifndef _PHYSIKS_WORLD_H_
#define _PHYSIKS_WORLD_H_

#include <physiksbase/objects.h>
#include <physiksbase/computations.h>
#include <iostream>
#include <vector>


namespace PhysiksBase {


using PhysiksBase::PhysicalObject;
using PhysiksBase::VectorField;
using std::vector;

/** A World encapsulates all information about fields, objects and parameters of the physical
 * environment. Force fields and objects (among other things) are registered in a specific
 * world instance, and integration is initiated via the instance itself. */
class World {
    public:
        /** the dimension of all tuples used for this world. */
        const int dimension;
        
        /** time step used for integration. Not constant to enable runtime modification. */
        double timestep;
        
        /** flag controlling whether or not data should be output (currently only to stdout) */
        bool output;

        /** \brief  Constructor 1.
         * \param dimension the dimension of all tuples used for this world
         * \param timestep the initial timestep for integration */
        World(int dimension, double timestep);

        /** \brief  Constructor 2.
         * \param init_objects An initialisation reference for this world's object table. */
        World(int dimension, double timestep, vector<PhysicalObject *> &init_objects);

        /** \brief  Constructor 3.
         * \param   init_force_fields   An initialisation reference for this world's force field
         *                              table. */
        World(int dimension, double timestep, vector<PhysicalObject *> &init_objects,
              vector<ForceField *> &init_force_fields);
        
        virtual ~World();

        /** \brief  Adds an object to the calling world's list of active objects.
         * \param obj the object to be added */
        void add_object(PhysiksBase::PhysicalObject &obj);
        
        void add_single_object_proc(void (*)(PhysicalObject *));
        void add_double_object_proc(void (*)(PhysicalObject *, PhysicalObject *));


        /** \brief  Commence integration for the calling world.
         * \param start_time the effective absolute time in the world when integration begins
         * \param end_time the effective absolute time in the world when integration stops */
        void integrate(double start_time, double end_time);
        
        /** \brief Performs a single iteration of the timestep and updates all world parameters. */
        void step();

        /** \brief Returns the current absolute time in the world.
         * \return the current absolute time */
        double current_time() const;
        
        /** \brief References the object at the specific index in the object table.
         * \param   index   the index of the queried object in this world's object table
         * \return  a pointer to the queried object. <b>The object is modifiable through the
         *          returned pointer.</b>    */
        PhysicalObject *object(unsigned int index) const;
        
        /** \brief  Returns the number of objects present in this world.
         * \return  the number of active objects in the world.  */
        unsigned int num_objects() const;
        
        // This is one way of implementing custom force fields, but it forces the user to muck
        // around with the internals of the argument. Not good.

        // User-defined unary procedures.
        vector<void (*)(PhysicalObject *)> single_object_procs;
        // User-defined binary procedures.
        vector<void (*)(PhysicalObject *, PhysicalObject *)> double_object_procs;

    private:
        /** A table of all objects in the world. */
        vector<PhysicalObject *> objects;

        /** A table of all defined force fields in the world. */
        vector<ForceField *> force_fields;
        
        /** A table of all defined accleration fields in the world. */
        vector<AccelerationField *> acceleration_fields;
        
        /** A table of all defined velocity fields in the world. */
        vector<VelocityField *> velocity_fields;
        
        /** \brief The current absolute time in the world. Will need to be discarded if someone
         *         decides to add relativistic computations to PhysiKs' capabilities. */
        double time;
        
        /** \brief Applies the user defined custom procedures for this world. */
        void apply_user_defined_procs();

        /** \brief Applies the user defined fields to all objects in the world's object table. */
        void apply_user_defined_fields();
        
        /** Please ignore. Needs to be deleted. */
        void print_data();
};


}   // End of namespace PhysiksBase

#endif
